#include "rdatetime.h"
#include "database.h"
#include "rnet.h"

database::database() :
	ms_login("")
{
	pthread_mutex_init( &m_lock, NULL ) ;
}

database::~database( )
{
	m_db.logoff( );
	pthread_mutex_destroy( &m_lock ) ;
	rlog << "pbj db quit ..." << endl;
}

string database::get_login_string( const string &s_db_name, const string &s_ip )
{
	map<string, map<string, string> > mp;
	map<string, map<string, string> >::iterator curiter = mp.end( );

	ifstream in( "/etc/odbc.ini", ios::binary );
	if( !in )
	{
		rlog << "/etc/odbc.ini does not exist!" << endl;
		return "";
	}

	string sLine;
	while( getline( in, sLine ) )
	{
		string sOdbcName;
		string::size_type pos = Substr( sOdbcName, sLine, "[", "]" );
		if( pos != string::npos )
		{
			pair<map<string, map<string, string> >::iterator, bool> pr = mp.insert( make_pair( sOdbcName, map<string, string>( ) ) );
			if( pr.second )
				curiter = pr.first;
			else
				curiter = mp.end( );
		}
		else
		{
			while( true )
			{
				string::size_type pos = 0;
				if( ( pos = sLine.find( '\r' ) ) != string::npos )
					sLine.erase( pos, 1 );
				else if( ( pos = sLine.find( ' ' ) ) != string::npos )
					sLine.erase( pos, 1 );
				else if( ( pos = sLine.find( '\t' ) ) != string::npos ) 
					sLine.erase( pos, 1 );
				else
					break;
			}

			if( curiter != mp.end( ) )
			{
				pair<string, string> pr = divideString( sLine, "=" );

				if( pr.first.empty( ) == false ) 
					curiter->second.insert( pr );
			}
		}
	}

	for( map<string, map<string, string> >::iterator iter = mp.begin( ); iter != mp.end( ); ++iter )
	{
		map<string, string> &submp = iter->second;
		string sServerIp = submp["Server"];
		if( sServerIp != s_ip )
			continue;

		string sDatabase = submp["Database"];
		if( sDatabase != s_db_name ) 
			continue;

		return submp["USER"] + "/" + submp["PASSWORD"] + "@" + iter->first;
	}

	return "";
}

bool database::db_connect( const string &s_db_name, const string &s_ip )
{
	ms_login = database::get_login_string( s_db_name, s_ip );
	if( ms_login.empty( ) )
	{
		rlog << "ms_login empty" << endl ;
		return false;	
	}

	rlog << ms_login << endl ;
	
	try
	{
		otl_connect::otl_initialize( 1 ); 

		m_db.rlogon( ms_login.c_str( ) );
		rlog << "DB connect done!" << endl;

		return true;
	}
	catch( otl_exception &err )
	{ 
		show_sql_error( err, cout );

		rlog << s_db_name << " db connect error!" << endl;
		return false;
	}

	return true ;
}

void *database::routine_keepalive(void* arg)
{
	pthread_detach(pthread_self());

	if ( arg == NULL )
	{
		rlog << "arg = NULL" << endl ; 
		return NULL ;
	}

	database* p_this = reinterpret_cast<database*>( arg );
	p_this->entry_keepalive(arg);

	return NULL;	
}

void database::entry_keepalive(  void* arg ) 
{
	pthread_detach(pthread_self());

	if ( arg == NULL )
	{
		rlog << "arg = NULL" << endl ; 
		return ;
	}

	rlog << "pbj db keepalive thread running ..." << endl ;
	
	while( true )
	{
		try
		{
			string s_sql = "select count( * ) from mysql.user";
			rmutex rlock( m_lock );
			otl_cursor::direct_exec( m_db, s_sql.c_str( ), otl_exception::enabled );
		}
		catch( otl_exception &err )
		{ 
			show_sql_error( err, cout );

			database* p_this = reinterpret_cast<database*>( arg );
			p_this->m_db.logoff( );
			p_this->m_db.rlogon( ms_login.c_str( ) );

			sleepx( 1 );
			continue;
		}

		sleepx( 60 * 60 );	
	}
}

bool database::background_routines( void )
{
	pthread_t tid;
	if ( pthread_create( &tid, NULL, routine_keepalive, this ) < 0 )
	{
		rlog << "pthread_create() : " << strerror(errno) << endl ;
		return false ;
	}

	return true;
}

bool database::init( const string &s_db_name, const string &s_ip )
{
	if ( !db_connect( s_db_name, s_ip ) )
	{
		rlog << "database::db_connect() error" << endl ;
		return false ;
	}
	
	if ( !background_routines() )
	{
		rlog << "database::background_routines() error" << endl ;
		return false ;
	}
	
	return true ;
}

bool database::get_daemon_version( const string& s_sub_vender, string& s_version, string& s_path ) 
{
	s_version.clear() ;
	s_path.clear();
	
	try
	{
		string s_sql = "select version, path from apps_update where type=0 ";
		if ( !s_sub_vender.empty() )
			s_sql += "and sub_vender='" + s_sub_vender + "'";
		rlog << s_sql << endl ;
		rmutex rlock( m_lock );
		otl_stream os( 1, s_sql.c_str( ), m_db );
		os >> s_version >> s_path ;
		os.close( );
	}
	catch( otl_exception &err )
	{
		show_sql_error( err, cout );
		return false ;
	}

	if ( s_version.empty() || s_path.empty() )
	{
		rlog << "db get daemon version or path empty" << endl ;
		return false ;
	}	

	return true;
}

bool database::get_control_version( const string& s_sub_vender, string& s_version, string& s_path ) 
{
	s_version.clear() ;
	s_path.clear();
	
	try
	{
		string s_sql = "select version, path from apps_update where type=1 ";
		if ( !s_sub_vender.empty() )
			s_sql += "and sub_vender='" + s_sub_vender + "'";
		rlog << s_sql << endl ;
		rmutex rlock( m_lock );
		otl_stream os( 1, s_sql.c_str( ), m_db );
		os >> s_version >> s_path ;
		os.close( );
	}
	catch( otl_exception &err )
	{
		show_sql_error( err, cout );
		return false ;
	}

	if ( s_version.empty() || s_path.empty() )
	{
		rlog << "db get control version or path empty" << endl ;
		return false ;
	}	

	return true;
}

bool database::get_display_version( const string& s_sub_vender, string& s_version, string& s_path ) 
{
	s_version.clear() ;
	s_path.clear();
	
	try
	{
		string s_sql = "select version, path from apps_update where type=2 ";
		if ( !s_sub_vender.empty() )
			s_sql += "and sub_vender='" + s_sub_vender + "'";
		rlog << s_sql << endl ;
		rmutex rlock( m_lock );
		otl_stream os( 1, s_sql.c_str( ), m_db );
		os >> s_version >> s_path ;
		os.close( );
	}
	catch( otl_exception &err )
	{
		show_sql_error( err, cout );
		return false ;
	}

	if ( s_version.empty() || s_path.empty() )
	{
		rlog << "db get display version or path empty" << endl ;
		return false ;
	}	

	return true;
}


